/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { readFileSync } from 'fs'
import ts from 'typescript'
import { AnalyzeResult, NodeInfo, PluginConfig } from '../models'
import { logger } from './Logger'

// 参数类型定义
interface ParamConfig {
  key: string
  type: string
  default: any
}

// ets文件解析
export class EtsAnalyzer {
  // 文件路径
  sourcePath: string
  // hvigor配置
  pluginConfig: PluginConfig
  // 解析结果
  analyzeResult: AnalyzeResult = new AnalyzeResult()
  // 是否存在装饰器
  routerAnnotationExisted: boolean = false
  // 过滤标识
  filterId: string = ''

  constructor(pluginConfig: PluginConfig, sourcePath: string) {
    this.pluginConfig = pluginConfig
    this.sourcePath = sourcePath
    this.filterId = `@${pluginConfig.annotation}(`
  }

  /**
   * 开始解析文件
   */
  start(): void {
    // 读取文件
    const sourceCode = readFileSync(this.sourcePath, 'utf-8')
    
    // 过滤没有指定装饰器的文件
    if (sourceCode.indexOf(this.filterId) === -1) {
      logger.debug(`文件不包含装饰器: ${this.sourcePath}`, 'EtsAnalyzer')
      return
    }
    
    logger.debug(`开始解析文件: ${this.sourcePath}`, 'EtsAnalyzer')
    
    // 解析文件，生成节点树信息
    const sourceFile = ts.createSourceFile(
      this.sourcePath,
      sourceCode,
      ts.ScriptTarget.ES2021,
      false
    )
    
    // 遍历节点信息
    ts.forEachChild(sourceFile, (node: ts.Node) => this.resolveNode(node))
    
    if (this.routerAnnotationExisted) {
      logger.debug(`文件包含有效装饰器: ${this.sourcePath}`, 'EtsAnalyzer')
      logger.debug(`解析结果: ${JSON.stringify(this.analyzeResult)}`, 'EtsAnalyzer')
    }
  }

  /**
   * 解析节点
   * @param node TS节点
   * @returns 节点信息
   */
  resolveNode(node: ts.Node): NodeInfo | undefined {
    switch (node.kind) {
      case ts.SyntaxKind.ImportDeclaration:
        // import节点不需要处理
        break
      case ts.SyntaxKind.MissingDeclaration:
        this.visitChildren(node)
        break
      case ts.SyntaxKind.Decorator:
        this.resolveDecoration(node as ts.Decorator)
        break
      case ts.SyntaxKind.CallExpression:
        this.resolveCallExpression(node as ts.CallExpression)
        break
      case ts.SyntaxKind.ExpressionStatement:
        this.resolveExpression(node as ts.ExpressionStatement)
        break
      case ts.SyntaxKind.Identifier:
        return this.createNodeInfo((node as ts.Identifier).escapedText.toString())
      case ts.SyntaxKind.StringLiteral:
        return this.createNodeInfo((node as ts.StringLiteral).text)
      case ts.SyntaxKind.PropertyAssignment:
        return this.resolvePropertyAssignment(node as ts.PropertyAssignment)
    }
  }

  /**
   * 访问节点的所有子节点
   * @param node TS节点
   */
  private visitChildren(node: ts.Node): void {
    node.forEachChild((child) => this.resolveNode(child))
  }

  /**
   * 创建节点信息
   * @param value 节点值
   * @returns 节点信息对象
   */
  private createNodeInfo(value: any): NodeInfo {
    const info = new NodeInfo()
    info.value = value
    return info
  }

  /**
   * 解析装饰器节点
   * @param decorator 装饰器节点
   */
  private resolveDecoration(decorator: ts.Decorator): void {
    // 判断表达式是否是函数调用
    if (decorator.expression.kind !== ts.SyntaxKind.CallExpression) {
      return
    }

    const callExpression = decorator.expression as ts.CallExpression
    
    // 表达式类型是否是标识符
    if (callExpression.expression.kind !== ts.SyntaxKind.Identifier) {
      return
    }

    const identifier = callExpression.expression as ts.Identifier
    
    // 标识符是否是自定义的装饰器
    if (identifier.text !== this.pluginConfig.annotation) {
      return
    }

    this.routerAnnotationExisted = true
    logger.debug(`找到装饰器: ${identifier.text}`, 'EtsAnalyzer')
    
    // 装饰器无参数的情况 - 这是有效的
    if (!callExpression.arguments || callExpression.arguments.length === 0) {
      logger.debug(`装饰器无参数，使用默认配置`, 'EtsAnalyzer')
      return
    }
    
    const arg = callExpression.arguments[0]
    
    // 装饰器有参数但不是对象字面量的情况 - 这是无效的
    if (arg && arg.kind !== ts.SyntaxKind.ObjectLiteralExpression) {
      logger.warn(`装饰器参数无效: 参数必须是对象字面量`, 'EtsAnalyzer')
      return
    }

    const properties = (arg as ts.ObjectLiteralExpression).properties
    
    // 遍历装饰器中的所有参数
    properties.forEach((property) => {
      if (property.kind !== ts.SyntaxKind.PropertyAssignment) {
        return
      }

      const propAssignment = property as ts.PropertyAssignment
      const propName = (propAssignment.name as ts.Identifier).escapedText as string
      
      logger.debug(`解析装饰器属性: ${propName}`, 'EtsAnalyzer')

      switch (propName) {
        case 'title':
          this.parseTitle(propAssignment)
          break
        case 'name':
          this.parseName(propAssignment)
          break
        case 'auth':
          this.parseAuth(propAssignment)
          break
        case 'param':
          this.parseParams(propAssignment)
          break
        default:
          logger.warn(`未知的装饰器属性: ${propName}，将被忽略`, 'EtsAnalyzer')
          break
      }
    })
  }

  /**
   * 解析title属性
   * @param propAssignment 属性赋值节点
   */
  private parseTitle(propAssignment: ts.PropertyAssignment): void {
    if (propAssignment.initializer.kind === ts.SyntaxKind.StringLiteral) {
      this.analyzeResult.title = (propAssignment.initializer as ts.StringLiteral).text
      logger.debug(`解析到标题: ${this.analyzeResult.title}`, 'EtsAnalyzer')
    }
  }

  /**
   * 解析name属性
   * @param propAssignment 属性赋值节点
   */
  private parseName(propAssignment: ts.PropertyAssignment): void {
    if (propAssignment.initializer.kind === ts.SyntaxKind.StringLiteral) {
      this.analyzeResult.name = (propAssignment.initializer as ts.StringLiteral).text
      logger.debug(`解析到路由名: ${this.analyzeResult.name}`, 'EtsAnalyzer')
    }
  }

  /**
   * 解析auth属性
   * @param propAssignment 属性赋值节点
   */
  private parseAuth(propAssignment: ts.PropertyAssignment): void {
    this.analyzeResult.auth = propAssignment.initializer.kind === ts.SyntaxKind.TrueKeyword
    logger.debug(`解析到鉴权配置: ${this.analyzeResult.auth}`, 'EtsAnalyzer')
  }

  /**
   * 解析参数
   * @param propAssignment 属性赋值节点
   */
  private parseParams(propAssignment: ts.PropertyAssignment): void {
    if (propAssignment.initializer.kind !== ts.SyntaxKind.ObjectLiteralExpression) {
      logger.warn('参数配置不是对象字面量', 'EtsAnalyzer')
      return
    }

    const paramProps = (propAssignment.initializer as ts.ObjectLiteralExpression).properties
    logger.debug(`解析参数, 数量: ${paramProps.length}`, 'EtsAnalyzer')
    
    paramProps.forEach((param) => {
      if (param.kind !== ts.SyntaxKind.PropertyAssignment) {
        return
      }

      const paramAssignment = param as ts.PropertyAssignment
      const paramName = (paramAssignment.name as ts.Identifier).text
      
      if (paramAssignment.initializer.kind !== ts.SyntaxKind.ObjectLiteralExpression) {
        logger.warn(`参数 ${paramName} 配置不是对象字面量`, 'EtsAnalyzer')
        return
      }

      const keyParams = (paramAssignment.initializer as ts.ObjectLiteralExpression).properties
      const paramConfig = this.extractParamConfig(keyParams)
      
      if (!paramConfig.key || !paramConfig.type) {
        logger.warn(`参数 ${paramName} 缺少必要属性 key 或 type`, 'EtsAnalyzer')
        return
      }

      // 设置默认值
      if (paramConfig.default === '' || paramConfig.default === 'undefined') {
        paramConfig.default = this.getDefaultValueForType(paramConfig.type)
      }

      // 构建参数表达式
      const paramExpression = this.buildParamExpression(paramConfig)
      this.analyzeResult.param.set(paramName, paramExpression)
      
      logger.debug(`解析到参数: ${paramName}, 类型: ${paramConfig.type}, 默认值: ${paramConfig.default}`, 'EtsAnalyzer')
    })
  }

  /**
   * 提取参数配置
   * @param keyParams 参数属性节点数组
   * @returns 参数配置对象
   */
  private extractParamConfig(keyParams: ts.NodeArray<ts.ObjectLiteralElementLike>): ParamConfig {
    const paramConfig: ParamConfig = {
      key: '',
      type: '',
      default: '',
    }

    keyParams.forEach((keyParam) => {
      if (keyParam.kind !== ts.SyntaxKind.PropertyAssignment) {
        return
      }

      const keyPropAssignment = keyParam as ts.PropertyAssignment
      const keyPropName = (keyPropAssignment.name as ts.Identifier).text

      switch (keyPropName) {
        case 'key':
          if (keyPropAssignment.initializer.kind === ts.SyntaxKind.StringLiteral) {
            paramConfig.key = (keyPropAssignment.initializer as ts.StringLiteral).text
          }
          break
        case 'type':
          if (keyPropAssignment.initializer.kind === ts.SyntaxKind.StringLiteral) {
            paramConfig.type = (keyPropAssignment.initializer as ts.StringLiteral).text
          }
          break
        case 'default':
          paramConfig.default = this.extractDefaultValue(keyPropAssignment.initializer)
          break
      }
    })

    return paramConfig
  }

  /**
   * 提取默认值
   * @param initializer 初始化器节点
   * @returns 默认值
   */
  private extractDefaultValue(initializer: ts.Expression): any {
    switch (initializer.kind) {
      case ts.SyntaxKind.TrueKeyword:
        return true
      case ts.SyntaxKind.FalseKeyword:
        return false
      case ts.SyntaxKind.NumericLiteral:
        return Number((initializer as ts.NumericLiteral).text)
      case ts.SyntaxKind.StringLiteral:
        return (initializer as ts.StringLiteral).text
      default:
        return ''
    }
  }

  /**
   * 获取类型的默认值
   * @param type 类型字符串
   * @returns 默认值
   */
  private getDefaultValueForType(type: string): any {
    switch (type) {
      case 'string':
        return ''
      case 'number':
        return 0
      case 'boolean':
        return true
      default:
        return ''
    }
  }

  /**
   * 构建参数表达式字符串
   * @param paramConfig 参数配置
   * @returns 参数表达式字符串
   */
  private buildParamExpression(paramConfig: ParamConfig): string {
    const defaultValueStr = paramConfig.type === 'string'
      ? `"${paramConfig.default}"`
      : paramConfig.default

    return `param?.${paramConfig.key} as ${paramConfig.type} || ${defaultValueStr}`
  }

  /**
   * 解析函数调用
   * @param callExpression 函数调用表达式
   */
  private resolveCallExpression(callExpression: ts.CallExpression): void {
    const identifier = this.resolveNode(callExpression.expression)
    if (identifier?.value === this.pluginConfig.annotation) {
      this.parseRouterConfig(callExpression.arguments)
    }
  }

  /**
   * 解析路由配置
   * @param args 函数参数
   */
  private parseRouterConfig(args: ts.NodeArray<ts.Expression>): void {
    args.forEach(arg => {
      if (arg.kind === ts.SyntaxKind.ObjectLiteralExpression) {
        (arg as ts.ObjectLiteralExpression).properties.forEach(prop => {
          this.parseConfig(prop)
        })
      }
    })
  }

  /**
   * 解析配置属性
   * @param node 属性节点
   */
  private parseConfig(node: ts.ObjectLiteralElementLike): void {
    const info = this.resolveNode(node)
    if (info?.value?.key && info?.value?.value) {
      Reflect.set(this.analyzeResult, info.value.key, info.value.value)
    }
  }

  /**
   * 解析表达式语句
   * @param statement 表达式语句
   */
  private resolveExpression(statement: ts.ExpressionStatement): void {
    if (!this.routerAnnotationExisted || this.analyzeResult?.viewName) {
      return
    }

    const identifier = this.resolveNode(statement.expression)
    if (!identifier?.value || identifier.value === 'struct') {
      return
    }

    // 提取视图名称和函数名
    this.analyzeResult.viewName = identifier.value
    const viewName = identifier.value.toString()
    this.analyzeResult.functionName = this.getComponentFunctionName(viewName)

    // 设置默认路由名
    if (!this.analyzeResult.name) {
      this.analyzeResult.name = `${this.pluginConfig.moduleName}/${this.analyzeResult.viewName}`
    }
    
    logger.debug(`解析到视图名: ${this.analyzeResult.viewName}, 函数名: ${this.analyzeResult.functionName}`, 'EtsAnalyzer')
  }

  /**
   * 从组件名获取函数名
   * @param viewName 组件名
   * @returns 函数名
   */
  private getComponentFunctionName(viewName: string): string {
    return `${viewName.charAt(0).toLowerCase()}${viewName.slice(1)}`
  }

  /**
   * 解析属性赋值
   * @param propertyAssignment 属性赋值节点
   * @returns 节点信息
   */
  private resolvePropertyAssignment(propertyAssignment: ts.PropertyAssignment): NodeInfo {
    const propertyName = this.resolveNode(propertyAssignment.name)?.value
    const propertyValue = this.resolveNode(propertyAssignment.initializer)?.value
    
    const info = new NodeInfo()
    info.value = {
      key: propertyName,
      value: propertyValue,
    }
    return info
  }
} 